package data.complex;

import data.base.Vector3;



public class Camera extends Ray{
  private double fov;
  private double aperature = 0.2;
  private double focalLength = 1;
  private Vector3 screenMiddleDirection;
  private Vector3 screenHorizontalDirection;
  private Vector3 screenVerticalDirection;
  private Vector3 screenHorizontalOffset;
  private Vector3 screenVerticalOffset;
  
  public Camera(Vector3 origin, double fov){
    super(origin);
    this.fov = fov;
  }
  
  public void initializeCameraPostion(){
    screenMiddleDirection = getDirection().clone();    
    screenMiddleDirection.normalize();
    
    screenHorizontalDirection = new Vector3(screenMiddleDirection.getZ(), 0, -screenMiddleDirection.getX());
    screenHorizontalDirection.setMagnitude(Math.tan(fov/360*Math.PI));
    screenHorizontalOffset = screenHorizontalDirection.clone();
    
    screenVerticalDirection = new Vector3(screenHorizontalDirection, screenMiddleDirection, false);
    screenVerticalDirection.setMagnitude(Math.tan(fov/360*Math.PI));
    screenVerticalOffset = screenVerticalDirection.clone();
  }
  
  public Ray getRay(double uFactor, double vFactor){
    Ray r = new Ray(clone());
    Vector3 direction = screenMiddleDirection.clone();
    direction.add(screenHorizontalDirection, uFactor);
    direction.add(screenVerticalDirection, vFactor);
    r.setDirection(direction);
    return r;
  }
  
  public Ray getMonteCarloRay(double uFactor, double vFactor){
    Ray r = getRay(uFactor, vFactor);
    r.setLength(focalLength);
    Vector3 target = r.getTarget();
    screenHorizontalOffset.setMagnitude((Math.random() - 0.5) * aperature);
    screenVerticalOffset.setMagnitude((Math.random() - 0.5) * aperature);
    
    Ray mcRay = new Ray(this);
    mcRay.add(screenHorizontalOffset);
    mcRay.add(screenVerticalOffset);
    mcRay.setTarget(target);  
    
    return mcRay;
  }

  public double getFov() {
    return fov;
  }

  public void setFov(double fov) {
    this.fov = fov;
  }
  
  public void autoFocus(VoxelSpace vs){
    if(vs != null){
      Ray r = new Ray(this);
      r.setDirection(getDirection().clone());    
      vs.calculateFirstTriangleIntersection(r, Double.MAX_VALUE, null);
      focalLength = r.getLength();
    }
  }
  
  public void setFocus(double focalLength){
    this.focalLength = focalLength;
  }
  
  public void setAperature(double aperature){
    this.aperature = aperature;
  }
}
